`ifndef EX_V
`define EX_V


`include "defines.v"

module ex(
	//from id_ex
	input  wire[`InstAddrWidth - 1 : 0] inst_addr_i,
	input  wire[`InstWidth - 1 : 0] 	inst_i,	
	input  wire[`RegAddrWidth - 1 : 0]  reg_waddr_i,	
	input  wire 	  					reg_wen_i,
	input  wire[`OPWidth - 1 : 0] 		op1_i,
	input  wire[`OPWidth - 1 : 0] 		op2_i,

	//to regs
	output reg[`RegAddrWidth - 1 : 0] 	waddr_o,
	output reg[`RegDataWidth - 1 : 0]	wdata_o,
	output reg 	    					wen_o,
	
	//to ctrl
	output reg[`InstAddrWidth - 1 : 0]	jump_addr_o,
	output reg   						jump_en_o,
	output reg  						hold_flag_o
);

wire[6:0] opcode; 
wire[4:0] rd; 
wire[2:0] funct3; 
wire[4:0] rs1;
wire[4:0] rs2;
wire[6:0] funct7;

assign opcode  = inst_i[6:0];
assign rd 	   = inst_i[11:7];
assign funct3  = inst_i[14:12];
assign rs1 	   = inst_i[19:15];
assign rs2 	   = inst_i[24:20];
assign funct7  = inst_i[31:25];
	
wire[`InstAddrWidth - 1 : 0] jump_imm = {{19{inst_i[31]}}, inst_i[31], inst_i[7], inst_i[30:25], inst_i[11:8], 1'b0};
wire  	   						op1_equal_op2;
	
assign	   op1_equal_op2 = (op1_i == op2_i) ? 1'b1 : 1'b0;

always @(*)begin
	case(opcode)
		`INST_TYPE_R : begin
			jump_addr_o = `INST_ZERO_ADDR;
			jump_en_o	= 1'b0;
			hold_flag_o = 1'b0;	
			case(funct3)				
				`INST_ADD, `INST_SUB : begin
					if(funct7 == 7'b0000000) begin //add
						wdata_o = op1_i + op2_i;
						waddr_o = reg_waddr_i;
						wen_o  = 1'b1;
					end
					else begin
						wdata_o = op2_i - op1_i;
						waddr_o = reg_waddr_i;
						wen_o  = 1'b1; 								
					end
				end

				default : begin
					wdata_o = `REG_ZERO;
					waddr_o = `REG_X0_ADDR;
					wen_o  = 1'b0;					
				end
			endcase
		end	

		`INST_TYPE_I : begin
			jump_addr_o = `INST_ZERO_ADDR;
			jump_en_o	= 1'b0;
			hold_flag_o = 1'b0;			
			case(funct3)				
				`INST_ADDI : begin
					wdata_o = op1_i + op2_i;
					waddr_o = reg_waddr_i;
					wen_o  = 1'b1;
				end

				default : begin
					wdata_o = `REG_ZERO;
					waddr_o = `REG_X0_ADDR;
					wen_o  = 1'b0;	
				end						
			endcase
		end	

		`INST_TYPE_SB : begin
			wdata_o = `REG_ZERO;
			waddr_o = `REG_X0_ADDR;
			wen_o  = 1'b0;		
			case(funct3)
				`INST_BEQ : begin
					jump_addr_o = (inst_addr_i + jump_imm) & {32{(op1_equal_op2)}}; 
					jump_en_o	= op1_equal_op2;
					hold_flag_o = 1'b0;					
				end					
				`INST_BNE : begin
					jump_addr_o = (inst_addr_i + jump_imm) & {32{(~op1_equal_op2)}};
					jump_en_o	= ~op1_equal_op2;
					hold_flag_o = 1'b0;					
				end

				default : begin
					jump_addr_o = `INST_ZERO_ADDR;
					jump_en_o	= 1'b0;
					hold_flag_o = 1'b0;					
				end
			endcase
		end

		`INST_JAL : begin
			wdata_o = inst_addr_i + 32'h4;
			waddr_o = reg_waddr_i;
			wen_o  = 1'b1;
			jump_addr_o = op1_i + inst_addr_i;
			jump_en_o	= 1'b1;
			hold_flag_o = 1'b0;				
		end

		`INST_LUI : begin
			wdata_o = op1_i;
			waddr_o = reg_waddr_i;
			wen_o  = 1'b1;
			jump_addr_o = `INST_ZERO_ADDR;
			jump_en_o	= 1'b0;
			hold_flag_o = 1'b0;			
		end

		default : begin
			wdata_o = `REG_ZERO;
			waddr_o = `REG_X0_ADDR;
			wen_o  = 1'b0;
			jump_addr_o = `INST_ZERO_ADDR;
			jump_en_o	= 1'b0;
			hold_flag_o = 1'b0;				
		end
	endcase
end

endmodule


`endif // EX_V